﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Markup;

namespace Geometry
{
    public sealed class MidPoint : GObjectBase, IGPoint, IDraggable
    {
        public MidPoint()
        {
            UpdateBoundingBox();
        }

        public MidPoint(IGPoint _point1, IGPoint _point2, double _ratio)
        {
            this.Point1 = _point1;
            this.Point2 = _point2;
            this.Ratio = _ratio;
            UpdateBoundingBox();
        }

        protected override void ReleaseDependencies()
        {
            this.Point1 = null;
            this.Point2 = null;
        }

        protected override void UpdateBoundingBox()
        {
            base.BoundingBox = new Rect(Position, Position);
        }

        private IGPoint m_Point1;
        public IGPoint Point1
        {
            get
            {
                return m_Point1;
            }
            set
            {
                if (m_Point1 == value) return;

                if (m_Point1 != null) m_Point1.PropertyChanged -= new PropertyChangedEventHandler(_related_PropertyChanged);
                m_Point1 = value;
                if (m_Point1 != null) m_Point1.PropertyChanged += new PropertyChangedEventHandler(_related_PropertyChanged);

                RaisePropertyChanged("Point1");
                UpdatePosition();
            }
        }

        private IGPoint m_Point2;
        public IGPoint Point2
        {
            get
            {
                return m_Point2;
            }
            set
            {
                if (m_Point2 == value) return;

                if (m_Point2 != null) m_Point2.PropertyChanged -= new PropertyChangedEventHandler(_related_PropertyChanged);
                m_Point2 = value;
                if (m_Point2 != null) m_Point2.PropertyChanged += new PropertyChangedEventHandler(_related_PropertyChanged);

                RaisePropertyChanged("Point2");
                UpdatePosition();
            }
        }

        private double m_Ratio;

        public double Ratio
        {
            get
            {
                return m_Ratio;
            }
            set
            {
                if (value.Equals(m_Ratio)) return;
                m_Ratio = value;
                RaisePropertyChanged("Ratio");
                UpdatePosition();
            }
        }

        void _related_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "Position") return;

            UpdatePosition();
        }

        private void UpdatePosition()
        {
            if (m_Point1.IsNull() || m_Point2.IsNull()) return;
            this.Position = new Point(
                m_Point1.PosX * m_Ratio + m_Point2.PosX * (1 - m_Ratio),
                m_Point1.PosY * m_Ratio + m_Point2.PosY * (1 - m_Ratio));

        }

        #region IGPoint Members
        private Point m_Position;

        public double PosX
        {
            get { return m_Position.X; }
            private set
            {
                if (value.Equals(m_Position.X)) return;
                m_Position.X = value;
                UpdateBoundingBox();
                RaisePropertyChanged("PosX");
                RaisePropertyChanged("Position");
            }
        }

        public double PosY
        {
            get { return m_Position.Y; }
            private set
            {
                if (value.Equals(m_Position.Y)) return;
                m_Position.Y = value;
                UpdateBoundingBox();
                RaisePropertyChanged("PosY");
                RaisePropertyChanged("Position");
            }
        }

        public Point Position
        {
            get { return m_Position; }
            private set
            {
                if (value.Equals(m_Position)) return;
                m_Position = value;
                UpdateBoundingBox();
                RaisePropertyChanged("Position");
                RaisePropertyChanged("PosX");
                RaisePropertyChanged("PosY");
            }
        }

        #endregion

        #region IDraggable Members
        private double m_BeginRatio;
        private bool m_IsDragged = false;
        private Vector m_TotalDrag;

        public void BeginDrag()
        {
            m_BeginRatio = m_Ratio;
            m_TotalDrag = new Vector();
        }

        public void CancelDrag()
        {
            this.Ratio = m_BeginRatio;
        }

        public void EndDrag()
        {
            m_IsDragged = false;
        }

        public void DragBy(Vector amount)
        {
            m_IsDragged = true;
            m_TotalDrag += amount;
            Vector axis = Point2.Position - Point1.Position;
            double angle = Vector.AngleBetween(axis, m_TotalDrag);
            double movement = Math.Cos(angle) * m_TotalDrag.Length;
            double newRatio = m_BeginRatio - movement / axis.Length;
            if (newRatio > 1) newRatio = 1.0;
            else if (newRatio < 0) newRatio = 0;
            this.Ratio = newRatio;
        }

        public bool IsDragged
        {
            get { return m_IsDragged; }
        }

        #endregion
    }
}
